/*
 *  This file is part of the Image Metadata Editor.
 *
 *  Copyright (C) 2015 Australian Institute of Marine Science
 *
 *  Contact: Gael Lafond <g.lafond@aims.org.au>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package au.gov.aims.exif.editor;

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.ColumnConstraintsBuilder;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import org.apache.commons.imaging.ImagingException;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

public class ImageEditor extends Application {
	private static final Logger LOGGER = Logger.getLogger(ImageEditor.class.getName());
	private static final String HELP_PAGE = "http://e-atlas.org.au/tools/image-metadata-editor";

	private static final int NB_FILES_WARNING = 10;

	// List of all loaded files. The application currently only support one file at the time.
	private ExifWrapper exifWrapper = null;

	// Fields references.
	// NOTE: There is probably a better way to achieve this, but this solution works,
	//     does not add too much overhead and it easy to understand.
	private Stage primaryStage = null;
	private Button saveButton = null;
	private Button helpButton = null;
	private Label fileNameLabel = null;
	private ImageView preview = null;
	private TextField titleField = null;
	private TextArea descriptionField = null;
	private TextField dateField = null;
	private TextField locationNameField = null;
	private TextField gpsLonField = null;
	private TextField gpsLatField = null;
	private TextField gpsRadiusField = null;
	private TextField photographersField = null;
	private TextField attributionField = null;
	private ComboBox<String> licenceField = null;
	private TextField keywordsField = null;

	private TextField reasonNotCCField = null;
	private Label reasonNotCCLabel = null;

	public static void main(String[] args) {
		launch(args);
	}

	@Override
	public void start(final Stage primaryStage) {
		this.start(primaryStage, null);
	}

	/**
	 * Start the application or spawn a new window.
	 * @param primaryStage Application stage (window instance)
	 * @param exifWrapper The EXIF wrapper for the image file to load. Set it to null to start the application
	 *     without file. This parameter is used to spawn multiple windows when the
	 *     user choose to open multiple files.
	 */
	public void start(final Stage primaryStage, ExifWrapper exifWrapper) {
		// When the user choose multiple file, the application spawn one window per file.
		// This method is called with each file (paramFile).
		if (exifWrapper != null) {
			if (exifWrapper.isEmpty()) {
				this.exifWrapper = null;
				// The file is invalid. Stop the application, do not show the window.
				// The window has not been shown yet so no need to close it.
				// I call the method close here just in case the library need it to free some variables.
				primaryStage.close();
				return;
			}
			this.exifWrapper = exifWrapper;
		}

		this.primaryStage = primaryStage;

		// Definition of the GUI
		primaryStage.setTitle(FXUtils.getWindowTitle());
		primaryStage.setMinWidth(400);
		primaryStage.setMinHeight(700);
		primaryStage.getIcons().add(FXUtils.getResourceImage("icon.png"));

		this.helpButton = new Button("Help");
		this.helpButton.setTooltip(new Tooltip("Open the help web page (Ctrl + F1)"));
		this.helpButton.setOnAction(new EventHandler<ActionEvent>() {
			@Override
			public void handle(ActionEvent event) {
				ImageEditor.this.getHostServices().showDocument(HELP_PAGE);
			}
		});

		this.saveButton = new Button("Save image");
		this.saveButton.setTooltip(new Tooltip("Save the image metadata (Ctrl + S)"));
		this.saveButton.setOnAction(new EventHandler<ActionEvent>() {
			@Override
			public void handle(ActionEvent event) {
				try {
					ImageEditor.this.save();
				} catch (Exception ex) {
					FXUtils.getNotificationWindow().showError("An exception occurred while saving the file.");
					LOGGER.log(Level.SEVERE, "An exception occurred while saving the file.", ex);
				}
			}
		});
		this.saveButton.setDisable(true);

		BorderPane body = new BorderPane();
		//body.setTop(FXUtils.createApplicationHeader());

		GridPane grid = new GridPane();
		grid.getColumnConstraints().addAll(
				ColumnConstraintsBuilder.create().minWidth(100).build(),
				ColumnConstraintsBuilder.create().hgrow(Priority.ALWAYS).build());
		grid.setPadding(new Insets(10, 10, 10, 10));
		grid.setVgap(5);
		grid.setHgap(5);


		int row = 0;
		this.fileNameLabel = new Label();
		String fontFamily = this.fileNameLabel.getFont().getFamily();
		double fontSize = this.fileNameLabel.getFont().getSize() * 0.9;
		this.fileNameLabel.setFont(Font.font(fontFamily, FontPosture.ITALIC, fontSize));
		this.fileNameLabel.setPrefHeight(20);
		this.fileNameLabel.setWrapText(true);
		GridPane.setConstraints(this.fileNameLabel, 0, row, 2, 1);
		GridPane.setMargin(this.fileNameLabel, new Insets(10, 0, 0, 0));
		GridPane.setHgrow(this.fileNameLabel, Priority.ALWAYS);
		grid.getChildren().add(this.fileNameLabel);


		row++;
		this.preview = new ImageView();
		GridPane.setConstraints(this.preview, 0, row, 2, 1);
		GridPane.setHalignment(this.preview, HPos.CENTER);
		grid.getChildren().add(this.preview);


		row++;
		this.titleField = new TextField();
		this.titleField.setPromptText(EAtlasField.TITLE.getDescription());
		Label titleLabel = new Label(EAtlasField.TITLE.getLabel());
		// NOTE: Label.setLabelFor seems to only add semantic value.
		titleLabel.setLabelFor(this.titleField);
		GridPane.setConstraints(titleLabel, 0, row);
		GridPane.setConstraints(this.titleField, 1, row);
		grid.getChildren().addAll(titleLabel, this.titleField);


		row++;
		this.descriptionField = new TextArea();
		this.descriptionField.setWrapText(true);
		this.descriptionField.setPromptText(EAtlasField.DESCRIPTION.getDescription());
		Label descLabel = new Label(EAtlasField.DESCRIPTION.getLabel());
		descLabel.setLabelFor(this.descriptionField);
		GridPane.setConstraints(descLabel, 0, row);
		GridPane.setValignment(descLabel, VPos.TOP);
		GridPane.setVgrow(descLabel, Priority.ALWAYS);
		GridPane.setMargin(descLabel, new Insets(5, 0, 0, 0));
		GridPane.setConstraints(this.descriptionField, 1, row);
		grid.getChildren().addAll(descLabel, this.descriptionField);


		row++;
		this.dateField = new TextField();
		this.dateField.setPromptText(EAtlasField.DATE.getDescription());
		Label dateLabel = new Label(EAtlasField.DATE.getLabel());
		// NOTE: Label.setLabelFor seems to only add semantic value.
		dateLabel.setLabelFor(this.dateField);
		GridPane.setConstraints(dateLabel, 0, row);
		GridPane.setConstraints(this.dateField, 1, row);
		grid.getChildren().addAll(dateLabel, this.dateField);


		row++;
		this.locationNameField = new TextField();
		this.locationNameField.setPromptText(EAtlasField.LOCATION_NAME.getDescription());
		Label locationNameLabel = new Label(EAtlasField.LOCATION_NAME.getLabel());
		locationNameLabel.setLabelFor(this.locationNameField);
		GridPane.setConstraints(locationNameLabel, 0, row);
		GridPane.setConstraints(this.locationNameField, 1, row);
		grid.getChildren().addAll(locationNameLabel, this.locationNameField);


		row++;
		Label gpsLabel = new Label("GPS:");
		HBox gpsBox = new HBox();

		this.gpsLonField = new TextField();
		this.gpsLonField.setPromptText(EAtlasField.LONGITUDE.getDescription());
		Label gpsLonLabel = new Label(EAtlasField.LONGITUDE.getLabel());
		gpsLonLabel.setLabelFor(this.gpsLonField);
		gpsLonLabel.setMinWidth(30);
		HBox.setMargin(gpsLonLabel, new Insets(3, 3, 0, 0));
		HBox.setHgrow(this.gpsLonField, Priority.ALWAYS);

		this.gpsLatField = new TextField();
		this.gpsLatField.setPromptText(EAtlasField.LATITUDE.getDescription());
		Label gpsLatLabel = new Label(EAtlasField.LATITUDE.getLabel());
		gpsLatLabel.setLabelFor(this.gpsLatField);
		gpsLatLabel.setMinWidth(30);
		HBox.setMargin(gpsLatLabel, new Insets(3, 3, 0, 50));
		HBox.setHgrow(this.gpsLatField, Priority.ALWAYS);

		this.gpsRadiusField = new TextField();
		this.gpsRadiusField.setPromptText(EAtlasField.RADIUS.getDescription());
		Label gpsRadiusLabel = new Label(EAtlasField.RADIUS.getLabel());
		gpsRadiusLabel.setLabelFor(this.gpsRadiusField);
		gpsRadiusLabel.setMinWidth(30);
		HBox.setMargin(gpsRadiusLabel, new Insets(3, 3, 0, 50));

		Label gpsRadiusUnitLabel = new Label("km");
		gpsRadiusUnitLabel.setMinWidth(5);
		HBox.setMargin(gpsRadiusUnitLabel, new Insets(3, 3, 0, 3));

		HBox.setHgrow(this.gpsRadiusField, Priority.ALWAYS);

		gpsBox.getChildren().addAll(gpsLonLabel, this.gpsLonField, gpsLatLabel, this.gpsLatField, gpsRadiusLabel, this.gpsRadiusField, gpsRadiusUnitLabel);
		GridPane.setConstraints(gpsLabel, 0, row);
		GridPane.setConstraints(gpsBox, 1, row);
		grid.getChildren().addAll(gpsLabel, gpsBox);

		row++;
		this.photographersField = new TextField();
		this.photographersField.setPromptText(EAtlasField.PHOTOGRAPHERS.getDescription());
		Label photographersLabel = new Label(EAtlasField.PHOTOGRAPHERS.getLabel());
		photographersLabel.setLabelFor(this.photographersField);
		GridPane.setConstraints(photographersLabel, 0, row);
		GridPane.setConstraints(this.photographersField, 1, row);
		grid.getChildren().addAll(photographersLabel, this.photographersField);

		row++;
		this.attributionField = new TextField();
		this.attributionField.setPromptText(EAtlasField.ATTRIBUTION.getDescription());
		Label attributionLabel = new Label(EAtlasField.ATTRIBUTION.getLabel());
		attributionLabel.setLabelFor(this.attributionField);
		GridPane.setConstraints(attributionLabel, 0, row);
		GridPane.setConstraints(this.attributionField, 1, row);
		grid.getChildren().addAll(attributionLabel, this.attributionField);

		row++;
		this.licenceField = new ComboBox<String>();
		this.licenceField.setConverter(new LicenceConverter());
		this.licenceField.getItems().addAll(EAtlasField.LICENCES.keySet());
		this.licenceField.valueProperty().addListener(new ChangeListener<String>() {
			@Override
			public void changed(ObservableValue ov, String t, String t1) {
				ImageEditor.this.toggleReasonNotCCField();
			}
		});
		// Select the default value
		this.licenceField.getSelectionModel().select(EAtlasField.DEFAULT_LICENCE_KEY);
		Label licenceLabel = new Label(EAtlasField.LICENCE.getLabel());
		licenceLabel.setLabelFor(this.licenceField);
		GridPane.setConstraints(licenceLabel, 0, row);
		GridPane.setConstraints(this.licenceField, 1, row);
		grid.getChildren().addAll(licenceLabel, this.licenceField);

		row++;
		this.reasonNotCCField = new TextField();
		this.reasonNotCCField.setPromptText(EAtlasField.REASON_NOT_CC.getDescription());
		this.reasonNotCCLabel = new Label(EAtlasField.REASON_NOT_CC.getLabel());
		this.reasonNotCCLabel.setLabelFor(this.reasonNotCCField);
		GridPane.setConstraints(this.reasonNotCCLabel, 0, row);
		GridPane.setConstraints(this.reasonNotCCField, 1, row);
		grid.getChildren().addAll(this.reasonNotCCLabel, this.reasonNotCCField);
		this.toggleReasonNotCCField();

		row++;
		this.keywordsField = new TextField();
		this.keywordsField.setPromptText(EAtlasField.KEYWORDS.getDescription());
		Label keywordsLabel = new Label(EAtlasField.KEYWORDS.getLabel());
		keywordsLabel.setLabelFor(this.keywordsField);
		GridPane.setConstraints(keywordsLabel, 0, row);
		GridPane.setConstraints(this.keywordsField, 1, row);
		grid.getChildren().addAll(keywordsLabel, this.keywordsField);

		row++;
		HBox buttonsBox = new HBox();
		buttonsBox.setSpacing(20);
		buttonsBox.getChildren().addAll(helpButton, this.saveButton);
		buttonsBox.setAlignment(Pos.BOTTOM_RIGHT);
		GridPane.setConstraints(buttonsBox, 0, row, 2, 1);
		grid.getChildren().add(buttonsBox);

		body.setCenter(grid);

		Scene scene = new Scene(body);

		// Fire the save button when Ctrl-S is pressed
		scene.getAccelerators().put(
			new KeyCodeCombination(KeyCode.S, KeyCombination.CONTROL_DOWN),
			new Runnable() {
				@Override public void run() {
					if (!ImageEditor.this.saveButton.isDisabled()) {
						ImageEditor.this.saveButton.fire();
					}
				}
			}
		);
		// Fire the help button when Ctrl-F1 is pressed
		// NOTE: There is a bug in JavaFX that prevent using F1 alone.
		scene.getAccelerators().put(
			new KeyCodeCombination(KeyCode.F1, KeyCombination.CONTROL_DOWN),
			new Runnable() {
				@Override public void run() {
					ImageEditor.this.helpButton.fire();
				}
			}
		);

		// Drag and drop file to the editor (this is just an alternative way to load an image; they are normally loaded from the CSVEditor)
		scene.setOnDragOver(new EventHandler<DragEvent>() {
			@Override
			public void handle(DragEvent event) {
				Dragboard db = event.getDragboard();
				if (db.hasFiles()) {
					event.acceptTransferModes(TransferMode.COPY);
				} else {
					event.consume();
				}
			}
		});
		scene.setOnDragDropped(new EventHandler<DragEvent>() {
			@Override
			public void handle(DragEvent event) {
				Dragboard db = event.getDragboard();
				boolean success = false;
				if (db.hasFiles()) {
					success = true;
					ImageEditor.this.loadFiles(db.getFiles());
				}
				event.setDropCompleted(success);
				event.consume();
			}
		});

		primaryStage.setScene(scene);

		if (this.exifWrapper != null) {
			this.loadValues();
		}

		primaryStage.show();

		// Bring the notification window to front, if there is message to be displayed.
		FXUtils.getNotificationWindow().toFront();
	}

	private void toggleReasonNotCCField() {
		if (this.reasonNotCCField != null && this.licenceField != null) {
			String licence = StringUtils.trim(this.licenceField.getSelectionModel().getSelectedItem());
			if (StringUtils.isEmpty(licence) || licence.equals(EAtlasField.COPYRIGHT_KEY)) {
				this.reasonNotCCField.setDisable(false);
				this.reasonNotCCLabel.setDisable(false);
			} else {
				this.reasonNotCCField.setDisable(true);
				this.reasonNotCCLabel.setDisable(true);
			}
		}
	}

	private void loadFiles(final List<File> chosenFiles) {
		if (chosenFiles != null) {
			if (chosenFiles.size() > NB_FILES_WARNING) {
				new FXUtils.ConfirmWindow(
						primaryStage,
						"Loading " + chosenFiles.size() + " files",
						"You choose " + chosenFiles.size() + " files. You should considered using the Batch editing.\n\n" +
						"Are you sure you want to load " + chosenFiles.size() + " files?", 300) {
					@Override
					public void handle(ActionEvent actionEvent) {
						ImageEditor.this.internalLoadFiles(chosenFiles);
					}
				}.show();
			} else {
				this.internalLoadFiles(chosenFiles);
			}
		}
	}

	private void internalLoadFiles(List<File> chosenFiles) {
		if (chosenFiles != null) {
			// If the application do not have a loaded file, it will load the first one.
			// Otherwise, the first file is loaded in a new window.
			// All other files are loaded in new windows.
			File loadFile = null;
			for (File chosenFile : chosenFiles) {
				if(chosenFile != null) {
					if (!chosenFile.exists()) {
						FXUtils.getNotificationWindow().showError("The file " + chosenFile.getAbsolutePath() + " do not exists.");
					} else if (!chosenFile.canRead()) {
						FXUtils.getNotificationWindow().showError("The application do not have sufficient privileges to read the file " + chosenFile.getAbsolutePath() + ".");
					} else if (!ExifWrapper.isSupportedImage(chosenFile)) {
						FXUtils.getNotificationWindow().showError("The file " + chosenFile.getAbsolutePath() + " is not supported.");
					} else {
						// Load file metadata to check if the file is valid. This add a bit of overhead bit give better feedback.
						ExifWrapper tmpExifWrapper = new ExifWrapper(chosenFile);
						if (tmpExifWrapper.isEmpty()) {
							FXUtils.getNotificationWindow().showError("The file " + chosenFile.getAbsolutePath() + " is not supported.");
						} else {
							if (loadFile == null && this.exifWrapper == null) {
								loadFile = chosenFile;
							} else {
								// Fire an other instance with File
								ImageEditor newEditor = new ImageEditor();
								newEditor.start(new Stage(), new ExifWrapper(chosenFile));
							}
						}
					}
				}
			}
			if (loadFile != null) {
				this.exifWrapper = new ExifWrapper(loadFile);
				if (this.exifWrapper.isEmpty()) {
					FXUtils.getNotificationWindow().showError("The file " + loadFile.getAbsolutePath() + " is not supported.");
					this.exifWrapper = null;
				}
				this.loadValues();
			}
		}
	}

	/**
	 * Load the form with the values of this exifWrapper
	 */
	private void loadValues() {
		if (this.exifWrapper == null) {
			this.resetForm();
		} else {
			File imageFile = this.exifWrapper.getImageFile();
			if (imageFile == null) {
				this.primaryStage.setTitle(FXUtils.getWindowTitle());
			} else {
				this.primaryStage.setTitle(FXUtils.getWindowTitle(imageFile.getName()));
				this.fileNameLabel.setText(imageFile.getAbsolutePath());

				InputStream imageInputStream = null;
				try {
					imageInputStream = new FileInputStream(imageFile);
					Image image = new Image(imageInputStream, 400, 400, true, true);
					this.preview.setImage(image);
				} catch(IOException ex) {
					FXUtils.getNotificationWindow().showWarning("Error loading the preview image");
					LOGGER.log(Level.WARNING, "Error loading the preview image", ex);
				} finally {
					if (imageInputStream != null) {
						try {
							imageInputStream.close();
						} catch(Exception ex) {
							FXUtils.getNotificationWindow().showWarning("Error closing the preview image");
							LOGGER.log(Level.WARNING, "Error closing the preview image", ex);
						}
					}
				}
			}

			this.titleField.setText(EAtlasField.TITLE.getValue(this.exifWrapper));
			this.descriptionField.setText(EAtlasField.DESCRIPTION.getValue(this.exifWrapper));
			this.dateField.setText(EAtlasField.DATE.getValue(this.exifWrapper));
			this.locationNameField.setText(EAtlasField.LOCATION_NAME.getValue(this.exifWrapper));

			Double[] gps = this.exifWrapper.getGPSValue();
			if (gps != null) {
				this.gpsLonField.setText(StringUtils.doubleToString(gps[ExifWrapper.LONGITUDE_IND]));
				this.gpsLatField.setText(StringUtils.doubleToString(gps[ExifWrapper.LATITUDE_IND]));
			} else {
				this.gpsLonField.setText(null);
				this.gpsLatField.setText(null);
			}

			this.gpsRadiusField.setText(EAtlasField.RADIUS.getValue(this.exifWrapper));

			String licence = EAtlasField.LICENCE.getValue(this.exifWrapper);
			if (!StringUtils.isEmpty(licence)) {
				this.licenceField.getSelectionModel().select(licence);
			}

			String keywords = EAtlasField.KEYWORDS.getValue(this.exifWrapper);
			if (!StringUtils.isEmpty(keywords)) {
				this.keywordsField.setText(keywords);
			}

			this.photographersField.setText(EAtlasField.PHOTOGRAPHERS.getValue(this.exifWrapper));
			this.attributionField.setText(EAtlasField.ATTRIBUTION.getValue(this.exifWrapper));
			this.reasonNotCCField.setText(EAtlasField.REASON_NOT_CC.getValue(this.exifWrapper));

			this.saveButton.setDisable(false);

			this.primaryStage.sizeToScene();
		}
	}

	private void resetForm() {
		this.primaryStage.setTitle(FXUtils.getWindowTitle());
		this.fileNameLabel.setText(null);
		this.preview.setImage(null);
		this.titleField.setText(null);
		this.descriptionField.setText(null);
		this.locationNameField.setText(null);
		this.gpsLonField.setText(null);
		this.gpsLatField.setText(null);

		this.photographersField.setText(null);
		this.attributionField.setText(null);
		this.licenceField.getSelectionModel().clearSelection();
		this.reasonNotCCField.setText(null);

		this.saveButton.setDisable(true);

		this.primaryStage.sizeToScene();
	}

	private void save() throws ImagingException, IOException {
		String lonStr = StringUtils.trim(this.gpsLonField.getText());
		String latStr = StringUtils.trim(this.gpsLatField.getText());
		String radiusStr = StringUtils.trim(this.gpsRadiusField.getText());
		String licence = StringUtils.trim(this.licenceField.getSelectionModel().getSelectedItem());
		String keywords = StringUtils.trim(this.keywordsField.getText());
		String reason = StringUtils.trim(this.reasonNotCCField.getText());
		String attribution = StringUtils.trim(this.attributionField.getText());
		String photographers = StringUtils.trim(this.photographersField.getText());

		EAtlasField.TITLE.setValue(this.exifWrapper, StringUtils.trim(this.titleField.getText()));
		EAtlasField.DESCRIPTION.setValue(this.exifWrapper, StringUtils.trim(this.descriptionField.getText()));
		EAtlasField.DATE.setValue(this.exifWrapper, StringUtils.trim(this.dateField.getText()));
		EAtlasField.LOCATION_NAME.setValue(this.exifWrapper, StringUtils.trim(this.locationNameField.getText()));

		boolean validGPS = ExifWrapper.preValidateGPS(lonStr, latStr, radiusStr);
		if (validGPS) {
			if (!StringUtils.isEmpty(lonStr) && !StringUtils.isEmpty(latStr)) {
				this.exifWrapper.setGPSValue(Double.valueOf(lonStr), Double.valueOf(latStr));
			} else {
				this.exifWrapper.setGPSValue(null);
			}
			EAtlasField.RADIUS.setValue(this.exifWrapper, radiusStr);
		}

		EAtlasField.PHOTOGRAPHERS.setValue(this.exifWrapper, photographers);
		EAtlasField.ATTRIBUTION.setValue(this.exifWrapper, attribution);
		EAtlasField.LICENCE.setValue(this.exifWrapper, licence);
		EAtlasField.KEYWORDS.setValue(this.exifWrapper, keywords);
		EAtlasField.REASON_NOT_CC.setValue(this.exifWrapper, reason);

		if (validGPS && this.exifWrapper.validate(false)) {
			try {
				// Save the values back in the file
				if (this.exifWrapper.save()) {
					// Reload the values from the file
					this.exifWrapper.reload();
					// Set the values in the form
					this.loadValues();
				}
			} catch (Exception ex) {
				FXUtils.getNotificationWindow().showError("Can not save the image " + exifWrapper.getImageFile().getAbsolutePath());
				LOGGER.log(Level.WARNING, "Can not save the image " + exifWrapper.getImageFile().getAbsolutePath(), ex);
			}
		}

		FXUtils.getNotificationWindow().showMessage("Image saved");
	}

	public class LicenceConverter extends StringConverter<String> {
		@Override
		public String toString(String key) {
			return EAtlasField.LICENCES.get(key);
		}

		@Override
		public String fromString(String label) {
			if (label == null) {
				return null;
			}
			for (Map.Entry<String, String> licenceEntry : EAtlasField.LICENCES.entrySet()) {
				if (label.equalsIgnoreCase(licenceEntry.getValue())) {
					return licenceEntry.getKey();
				}
			}
			return null;
		}
	}
}
